#include <cassert>
#include <cstring>
#include "memcheck.h"
#include "CCReactor.h"
#include "HttpListener.h"
#include "../common/decoder.h"
#include "../common/global.h"
#include "../common/socketNet.h"


HttpListener::HttpListener(NewConnCb new_conn_cb, int backlog) :
	_backlog (backlog),
	_newfd_cnt (0),
	_fd_array (NULL),
	_flag (0)
{
	_accept_cnt = 256;
	//calloc fd array
	_fd_array = (int*)malloc(_accept_cnt * sizeof(int));
	memset(_fd_array, -1, _accept_cnt * sizeof(int));
	_peer_array = (struct sockaddr_in*) malloc(_accept_cnt * sizeof(struct sockaddr_in));
	_new_conn_cb = new_conn_cb;
}

HttpListener::~HttpListener()
{
	//free fd array
	if (_fd_array) {
		free(_fd_array);
		_fd_array = NULL;
	}
	if (_peer_array) {
		free(_peer_array);
		_peer_array = NULL;
	}
}
 

int HttpListener::Listen(const char* ip, uint16_t port)
{
	strncpy(this->_bindAddr, ip, sizeof(_bindAddr)-1);
	this->_bindPort = port;

	if((netfd = CreateTcpSocket()) == -1)
	{
		GlobalUtils::LogError("create socket failed");
		return -1;
	}
	
	if(BindSocket(netfd, _bindAddr, _bindPort) != ERR_SUCCESS)
	{
		GlobalUtils::LogError("bind addr[" + std::string(_bindAddr) + "], port[" + std::to_string(_bindPort) + "] failed.");
		close(netfd);
		netfd = -1;
		return -1;
	}
	
	if(ListenSocket(netfd, _backlog) != ERR_SUCCESS)
	{
		GlobalUtils::LogError("listen failed");
		close(netfd);
		netfd = -1;
		return -1;
	}
	
	EnableInput();
	GlobalUtils::LogInfo("server fd[" + std::to_string(netfd) + "] bind addr[" + std::string(_bindAddr) + "], port[" + std::to_string(_bindPort) + "] listening...");
	return 0;
}


int HttpListener::InputNotify(void)
{
	struct sockaddr_in*  peer = _peer_array;
	socklen_t	peerSize;

	peerSize = sizeof (struct sockaddr_in);
	while (true)
	{
		int ret = proc_accept ( peer, &peerSize);
		
		if (_newfd_cnt <= 0)
		{
			GlobalUtils::LogDebug("invoke proc_accept failed, new fd count = " + std::to_string(_newfd_cnt));
			return ERR_SUCCESS; 
		}

		if (_newfd_cnt > 0)
		{
			proc_request ( peer );
		}

		if (ret < 0)
		{
			return ERR_SUCCESS;
		}
	}
}

int HttpListener::proc_accept(struct sockaddr_in* peer, socklen_t* peerSize)
{
    int newfd = -1;

    memset (_fd_array, -1, _accept_cnt * sizeof(int));
    _newfd_cnt  = 0;

    for (int i = 0; i < _accept_cnt; ++i)
    {
		newfd = ::accept (netfd, (struct sockaddr*) &peer[i], peerSize);
		
		if (newfd < 0)
		{
			if (errno == EINTR)
	     	{
	        	continue;
	    	}

	    	if (errno == EAGAIN)
	    	{
	       	 	return -1;
	   	 	}

			GlobalUtils::LogError("accept new connection failed, client[" + std::string(_bindAddr) + ":" + std::to_string(_bindPort) + "], fd[" + std::to_string(netfd) + "]");
			return -1;
	    }
		GlobalUtils::LogDebug("proc_accept: accept new connection fd[" + std::to_string(newfd) + "]");

	    _fd_array[i] = newfd;
	    _newfd_cnt++;
	}

    return 0;
}

int HttpListener::proc_request(struct sockaddr_in* peer)
{
	if (!_new_conn_cb)
	{
		return -1;
	}

    for (int i = 0; i < _newfd_cnt; ++i)
    {
        if (_fd_array[i] == -1)
        {
            continue;
        }

		int ret = _new_conn_cb(_fd_array[i], &peer[i]);
		if (ret != 0)
		{
			GlobalUtils::LogError("new conn cb handle failed, client[" + std::string(inet_ntoa(peer[i].sin_addr)) + ":" + std::to_string(peer[i].sin_port) + "], fd[" + std::to_string(netfd) + "], ret:" + std::to_string(ret));
			_fd_array[i] = -1;
			continue;
		}

		GlobalUtils::LogInfo("[" + std::string(_bindAddr) + ":" + std::to_string(_bindPort) + "] accept new tcp connection, client[" + std::string(inet_ntoa(peer[i].sin_addr)) + ":" + std::to_string(peer[i].sin_port) + "], connect fd[" + std::to_string(_fd_array[i]) + "]");
    }
    return 0;
}

int HttpListener::OutputNotify() {
    // Handle output events (not typically used by listeners)
    return 0;
}

int HttpListener::HangupNotify() {
    // Handle hangup events
    return 0;
}





